_Pragma("once")

#include "pch.hpp"
#include "fmt.hpp"

namespace sdl_engine::log {

class Logger final {
public:
    enum Level {
        TRACE = 0,
        INFO,
        WARNING,
        ERROR,
        NO_LOG,
    };

    inline static std::list<Logger>& GetLoggers() { return loggers; }

    static Logger& CreateCoutLog();
    static Logger& CreateCerrLog();
    static Logger& Create(const std::string& filename);

    /* @brief set all logger's level */
    inline static void SetAllLevel(Level level) {
        for (auto& logger: loggers)
            logger.SetLevel(level);
    }

    /* @brief set all logger's level. Other will be created logger will also have this level. */
    inline static void SetDefaultLevel(Level level) {
        defaultLevel = level;
        SetAllLevel(level);
    }

    inline void SetLevel(Level level) { level_ = level; }
    inline Level GetLevel() const { return level_; }

    Logger(std::ostream& o);
    Logger(const std::string& filename);
    ~Logger();

    Logger(const Logger&) = delete;
    Logger& operator=(const Logger&) = delete;

    void LogInfo(const char* filename,
                 const char* function,
                 int line,
                 const std::string& msg);
    void LogWarn(const char* filename,
                 const char* function,
                 int line,
                 const std::string& msg);
    void LogErr(const char* filename,
                const char* function,
                int line,
                const std::string& msg);
    void LogTrace(const char* filename,
                  const char* function,
                  int line,
                  const std::string& msg);

private:
    std::ostream& output_;
    std::ofstream file_;
    Level level_ = defaultLevel;

    void log(const char* filename,
            const char* function,
            int line,
            const char* tag,
            const std::string& msg);

    static std::list<Logger> loggers;
    static Level defaultLevel;
};

void Init(int argc, char** argv);

#define Logi(msg) do { for (auto& logger : ::sdl_engine::log::Logger::GetLoggers()) logger.LogInfo(__FILE__, __FUNCTION__, __LINE__, msg); } while(0)
#define Logw(msg) do { for (auto& logger : ::sdl_engine::log::Logger::GetLoggers()) logger.LogWarn(__FILE__, __FUNCTION__, __LINE__, msg); } while(0)
#define Loge(msg) do { for (auto& logger : ::sdl_engine::log::Logger::GetLoggers()) logger.LogErr(__FILE__, __FUNCTION__, __LINE__, msg); } while(0)
#define Logt(msg) do { for (auto& logger : ::sdl_engine::log::Logger::GetLoggers()) logger.LogTrace(__FILE__, __FUNCTION__, __LINE__, msg); } while(0)

}
